package com.shiyi.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.shiyi.common.RedisConstants;
import com.shiyi.common.ResponseResult;
import com.shiyi.config.satoken.MySaTokenListener;
import com.shiyi.config.satoken.OnlineUser;
import com.shiyi.dto.SystemUserDTO;
import com.shiyi.entity.FeedBack;
import com.shiyi.entity.Menu;
import com.shiyi.entity.User;
import com.shiyi.entity.UserInfo;
import com.shiyi.enums.LoginTypeEnum;
import com.shiyi.exception.BusinessException;
import com.shiyi.mapper.FeedBackMapper;
import com.shiyi.mapper.RoleMapper;
import com.shiyi.mapper.UserInfoMapper;
import com.shiyi.mapper.UserMapper;
import com.shiyi.service.FeedBackService;
import com.shiyi.service.MenuService;
import com.shiyi.service.RedisService;
import com.shiyi.service.UserService;
import com.shiyi.utils.AesEncryptUtils;
import com.shiyi.utils.PageUtils;
import com.shiyi.utils.UUIDUtils;
import com.shiyi.vo.SystemUserInfoVO;
import com.shiyi.vo.SystemUserVO;
import com.shiyi.vo.UserInfoVO;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.shiyi.common.ResultCode.ERROR_USER_NOT_EXIST;

/**
 * @author blue
 * @description:
 * @date 2021/7/30 17:25
 */
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);

    private final MenuService menuService;

    private final UserInfoMapper userInfoMapper;

    private final RoleMapper roleMapper;

    private final RedisService redisService;

    public final RedisTemplate<String,String> redisTemplate;

    public final FeedBackMapper feedBackMapper;

    public final FeedBackService feedBackService;

    @Autowired
    public UserService userService;
    ReentrantLock reentrantLock = new ReentrantLock();
    public Map<String, Object> EALAlarmRecord = new HashMap<>();
//    @PostConstruct
//    public void init() {
//        Map<String, User> map = new HashMap<>();
//        Map<String, String> cacheMap = redisTemplate.opsForHash().entries("EAL_ALARM");
//        for (Map.Entry<String, String> entry : cacheMap.entrySet()) {
//            String key = entry.getKey();
//            if (!entry.getValue().isEmpty()) {
//                User ealInfoDto = JSON.parseObject(entry.getValue(), User.class);
//                map.put(key, ealInfoDto);
//            }
//
//        }
//        if (!map.isEmpty()) {
//            // 将Redis数据写入到静态Map中
//            EALAlarmRecord.putAll(map);
//        }
//
//        Set<String> set = new HashSet<>();
//        set.add("tom1");
//        set.add("tom2");
//        set.add("tom3");
//        HashSet<ZSetOperations.TypedTuple> typedTuples = new HashSet<>();
//
//        redisTemplate.opsForZSet().add("zsetDemo",  "tom3", 15);
//        // 可以在这里打印日志来确认Map中的数据是否正确写入
//        log.info("Loaded Redis data into static Map: " + EALAlarmRecord);
//
//    }

    public static void main(String[] args) {
        String text = "国家核安全局（应急值班传真：0596-8570082、0596-8570068(测试传真)）";

        // 定义匹配格式的正则表达式
        String regex = "\\d+-\\d+";

        // 编译正则表达式模式
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(text);

        // 查找所有匹配的字符串并输出
        while (matcher.find()) {
            String group = matcher.group();
            String[] split = group.split("-");
            System.out.println(split[0]);
            System.out.println(split[1]);
        }
    }

    /**
     * 用户列表
     * @param username
     * @param loginType
     * @return
     */
    @Override
    public ResponseResult listUser(String username, Integer loginType) {
        Page<SystemUserInfoVO> page = baseMapper.selectPageRecord(new Page<>(PageUtils.getPageNo(), PageUtils.getPageSize()),username,loginType);
        return ResponseResult.success(page);
    }

    /**
     * 用户详情
     * @param id
     * @return
     */
    @Override
    public ResponseResult getUserById(String id) {
        SystemUserVO user = baseMapper.getById(id);
        return ResponseResult.success(user);
    }

    /**
     *  添加用户
     * @param dto
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResult insertUser(SystemUserDTO dto) {
        UserInfoVO userInfoVO = baseMapper.selectByUserName(dto.getUsername());
        if (userInfoVO != null) {
            throw new BusinessException("用户名已存在!");
        }
        //添加用户信息
        UserInfo userInfo = UserInfo.builder().nickname(dto.getNickname()).avatar(dto.getAvatar()).build();
        userInfoMapper.insert(userInfo);

        //添加用户账号
        User user = User.builder().username(dto.getUsername()).password(AesEncryptUtils.aesEncrypt(dto.getPassword()))
                .status(dto.getStatus()).userInfoId(userInfo.getId()).roleId(dto.getRoleId()).loginType(LoginTypeEnum.EMAIL.getType()).build();

        baseMapper.insert(user);
        redisService.sAdd(RedisConstants.APPLY_IMG, dto.getAvatar());
        return ResponseResult.success(user);
    }

    /**
     *  修改用户
     * @param user
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResult updateUser(SystemUserInfoVO user) {
        User user1 = User.builder()
                .id(user.getId())
                .roleId(user.getRoleId())
                .status(user.getStatus())
                .username(user.getUsername())
                .build();

        User user2 = baseMapper.selectById(user.getId());
        UserInfo userInfo = UserInfo.builder()
                .id(user2.getUserInfoId())
                .avatar(user.getAvatar())
                .nickname(user.getNickname())
                .build();
        baseMapper.updateById(user1);

        userInfoMapper.updateById(userInfo);
        redisService.sAdd(RedisConstants.APPLY_IMG, user.getAvatar());
        return ResponseResult.success();
    }

    /**
     * 删除用户
     * @param ids
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResult deleteBatch(List<String> ids) {
        //先删除用户账号
         baseMapper.deleteBatchIds(ids);
         //删除用户信息
        userInfoMapper.deleteByUserIds(ids);
        return ResponseResult.success();
    }

    /**
     * 获取当前登录用户详情
     * @return
     */
    @Override
    public ResponseResult getCurrentUserInfo() {
        return ResponseResult.success("获取当前登录用户信息成功", baseMapper.getById(StpUtil.getLoginIdAsString()));
    }

    /**
     * 获取当前登录用户所拥有的菜单权限
     * @return
     */
    @Override
    public ResponseResult getCurrentUserMenu() {
        List<Integer> menuIds = baseMapper.getMenuId(StpUtil.getLoginIdAsString());
        if (CollectionUtils.isEmpty(menuIds)){
            return ResponseResult.error("暂无权限");
        }
        List<Menu> menus = menuService.listByIds(menuIds);
        List<Menu> menuTree = menuService.listMenuTree(menus);
        return ResponseResult.success(menuTree);
    }

    /**
     * 修改密码
     * @param map
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResult updatePassword(Map<String, String> map) {

        User user = baseMapper.selectById(StpUtil.getLoginIdAsString());
        Assert.notNull(user,ERROR_USER_NOT_EXIST.getDesc());

        boolean isValid = AesEncryptUtils.validate(user.getPassword(),map.get("oldPassword"));
        Assert.isTrue(isValid,"旧密码校验不通过!");

        String newPassword = AesEncryptUtils.aesEncrypt(map.get("newPassword"));
        user.setPassword(newPassword);
        baseMapper.updateById(user);
        return ResponseResult.success("修改成功");
    }

    /**
     * 在线用户
     * @param keywords
     * @return
     */
    @Override
    public ResponseResult listOnlineUsers(String keywords) {
        int pageNo = PageUtils.getPageNo().intValue();
        int pageSize = PageUtils.getPageSize().intValue();

        List<OnlineUser> onlineUsers = MySaTokenListener.ONLINE_USERS;
        //根据关键词过滤
        if (StringUtils.isNotBlank(keywords)) {
            onlineUsers = MySaTokenListener.ONLINE_USERS.stream().filter(item -> item.getNickname().contains(keywords)).collect(Collectors.toList());
        }
        //排序
        onlineUsers.sort((o1,o2)->{
            if (o2.getLoginTime().getTime() > o1.getLoginTime().getTime()) {
                return 1;
            }else {
                return -1;
            }
        });
        int fromIndex = (pageNo-1) * pageSize;
        int toIndex = onlineUsers.size() - fromIndex > pageSize ? fromIndex + pageSize : onlineUsers.size();
        List<OnlineUser> userOnlineList = onlineUsers.subList(fromIndex, toIndex);
        logger.info("memory用户数：{}", userOnlineList.size());

        Map<String,Object> map = new HashMap<>();
        map.put("total",onlineUsers.size());
        map.put("records",userOnlineList);
        return ResponseResult.success(map);
    }

    /**
     * 踢人下线
     * @param token
     * @return
     */
    @Override
    public ResponseResult kick(String token) {
        logger.info("当前踢下线的用户token为:{}",token);
        StpUtil.kickoutByTokenValue(token);
        return ResponseResult.success();
    }

    @SneakyThrows
    @Override
//    @Async("threadPoolTaskExecutor")
//    @Transactional(rollbackFor = Exception.class)
    public void getMyTest() {
//        List<FeedBack> list = feedBackService.list();
//        for (int i = 0; i < list.size(); i++) {
//            FeedBack feedBack = new FeedBack();
//            feedBack.setId(list.get(i).getId());
//            feedBack.setUserId("abc" + i);
//            feedBack.setTitle("title" + i);
//            feedBack.setType(2);
//            feedBack.setStatus(0);
//            feedBack.setCreateTime(new Date());
//            feedBackMapper.updateById(feedBack);
//        }

        long l = System.currentTimeMillis();
        AtomicInteger count = new AtomicInteger();
        count.set(0);

        CompletableFuture<Void> one = CompletableFuture.runAsync(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            Random random = new Random();
            int i = random.nextInt(10);
            System.out.println("随机数："+i);
            count.set(count.get() + i);
            System.out.println("第一个计算结束:" + count);
        });
        CompletableFuture<Void> two = CompletableFuture.runAsync(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            Random random = new Random();
            int i = random.nextInt(10);
            System.out.println("随机数："+i);
            count.set(count.get() + i);
            System.out.println("第二个计算结束:" + count);
        });

        CompletableFuture.allOf(one, two).get();
        if (count.get() > 10){
            CompletableFuture.runAsync(()->{
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                Random random = new Random();
                int i = random.nextInt(10);
                count.set(count.get() + i);
                System.out.println("第三个计算结束:" + count);
            }).thenRunAsync(()->{
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                Random random = new Random();
                int i = random.nextInt(10);
                count.set(count.get() + i);
                System.out.println("第四个计算结束:" + count);
            }).get();
        }

        System.out.println("count==" + count);
        long l1 = System.currentTimeMillis();
        System.out.println("用时：" + (l1-l) + "ms");

    }
    @Override
//    @Async("threadPoolTaskExecutor")
//    @Transactional
    @SneakyThrows
    public void getMyTest1() {
        Long count = redisTemplate.opsForValue().increment("count", 150);

    }

    public void startClub() {
        Long count = redisTemplate.opsForValue().increment("countKey", 150);
        log.info("上架数量：{}",count);
        String uuid = UUIDUtils.getUuid();
        String key = uuid + "user01";
        redisTemplate.opsForValue().set(key,"orderToken",10, TimeUnit.MINUTES);
        log.info("发放订单token：{}",key);
    }


    public boolean acquireLock(String lockKey, String requestId, long expireTime) {
        Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, Duration.ofSeconds(expireTime));
        return result != null && result;
    }

    public void releaseLock(String lockKey, String requestId) {
        String value = redisTemplate.opsForValue().get(lockKey);
        if (value != null && value.equals(requestId)) {
            redisTemplate.delete(lockKey);
        }
    }


    @Override
    @Transactional
    public void order(String token) {
        String  key = token + "user01";
        boolean b = acquireLock("lock_key", key, 10);
        if (b){
            System.out.println("获取分布式锁");
            try {
                Object o = redisTemplate.opsForValue().get(key);
                if (o != null){
                    System.out.println("下单锁token验证成功");
                    FeedBack feedBack = new FeedBack();
                    feedBack.setTitle(Thread.currentThread().getName());
                    feedBack.setUserId(Thread.currentThread().getName());
                    feedBack.setType(2);
                    feedBack.setCreateTime(new Date());
                    feedBack.setStatus(0);
                    feedBackService.save(feedBack);
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println("下单成功");
                    Boolean delete = redisTemplate.opsForValue().getOperations().delete(key);
                    System.out.println(delete);
                }else {
                    System.out.println("请勿重复下单");
                }
                releaseLock("lock_key", key);
            }catch (Exception e){
                throw new RuntimeException();
            }finally {
                releaseLock("lock_key", key);
            }
        }else {
            System.out.println("请求频繁");
        }

    }

    @Override
    @Transactional
    public void dataSourceDeadLock() throws InterruptedException {
        System.out.println("开始第一个事务");
        Random random = new Random();
        int i = random.nextInt(2000);
        FeedBack feedBack = new FeedBack();
        feedBack.setId(14234);
//        feedBack.setTitle("b"  + i );
//        feedBackMapper.myUpdate(feedBack.getTitle(),feedBack.getId());
        log.error("第一个事务获取共享锁");
        List<FeedBack> feedBacks = feedBackMapper.selectAll();
        Thread.sleep(2000);
        FeedBack feedBack1 =feedBackMapper.selectByIdForUpdate(14234);
        Thread.sleep(1000);
//        FeedBack feedBack2 =feedBackMapper.selectByIdForUpdate(14234);
        log.error("第一个事务获取排他锁开始");
        feedBackMapper.myUpdate(feedBack1.getTitle(),feedBack.getId());
        Thread.sleep(1000);
        log.error("第一个事务获取排他锁完成");
        List<FeedBack> feedBacks3 = feedBackMapper.selectAll();
//        Thread.sleep(5000);
    }

    @Override
    @Transactional
    public void dataSourceDeadLock2() throws InterruptedException {
        System.out.println("开始第二个事务");
        Random random = new Random();
        int i = random.nextInt(2000);
        log.error("第二个事务获取共享锁");
        FeedBack feedBack1 =feedBackMapper.selectByIdForUpdate(14234);
        Thread.sleep(2000);
        feedBack1.setContent("bbb"+i);
        log.error("第二个事务获取排他锁开始 ");
        feedBackMapper.myUpdate(feedBack1.getContent() +  i,14234);
//        FeedBack feedBack1 =feedBackMapper.selectByIdForUpdate(14234);
//        feedBack1.setContent("a"  + i);
////        feedBackMapper.myUpdateContent(feedBack1.getContent(),feedBack.getUserId());
////        log.info(feedBack1.toString());
//        Thread.sleep(2000);
//        FeedBack feedBack2 =feedBackMapper.selectByIdForUpdate(14235);
        log.error("第二个事务获取排他锁完成：");
    }

    public void joinIn() {
//        Long countKey = redisTemplate.opsForValue().increment("countKey", -1);
        List<FeedBack> list = feedBackService.list();
        if (list.size() <= 100){
            FeedBack feedBack = new FeedBack();
            feedBack.setTitle(Thread.currentThread().getName());
            feedBack.setUserId(Thread.currentThread().getName());
            feedBack.setType(2);
            feedBack.setCreateTime(new Date());
            feedBack.setStatus(0);
            feedBackService.save(feedBack);
            System.out.println("报名成功");
        }else {
            System.out.println("无名额");
        }
    }
}
